package com.gitee.free.coding.admin.system.service.impl;

import com.gitee.free.coding.admin.system.entity.SystemUser;
import com.gitee.free.coding.admin.system.entity.SystemUserAuth;
import com.gitee.free.coding.admin.system.logic.SystemUserAuthLogic;
import com.gitee.free.coding.admin.system.logic.SystemUserLogic;
import com.gitee.free.coding.admin.system.mapstruct.mapper.SystemUserMapstructMapper;
import com.gitee.free.coding.admin.system.req.SystemLoginReq;
import com.gitee.free.coding.admin.system.res.body.SystemLoginResBody;
import com.gitee.free.coding.admin.system.res.body.SystemVerificationCodeResBody;
import com.gitee.free.coding.admin.system.service.ISystemUserAuthService;
import com.gitee.free.coding.core.components.RedisService;
import com.gitee.free.coding.core.enums.RedisKeyPrefixEnum;
import com.gitee.free.coding.core.exceptions.FreeRuntimeException;
import com.gitee.free.coding.core.utils.CaptchaGenerator;
import com.gitee.free.coding.core.utils.Encipher;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author liaoen
 * @since 2024-05-29 22:44:05
 */
@Slf4j
@Service
public class SystemUserAuthServiceImpl implements ISystemUserAuthService {

    @Autowired
    private SystemUserLogic systemUserLogic;

    @Autowired
    private SystemUserAuthLogic systemUserAuthLogic;

    @Autowired
    private RedisService redisService;

    @Override
    public SystemVerificationCodeResBody generateVerificationCode() {
        String uniqueIdentifier = UUID.randomUUID().toString();
        String verificationCode = CaptchaGenerator.generateRandomText4();
        redisService.setValueWithExpiration(RedisKeyPrefixEnum.getVerifyCode(uniqueIdentifier), verificationCode, 1, TimeUnit.MINUTES);

        SystemVerificationCodeResBody body = new SystemVerificationCodeResBody();
        body.setUniqueIdentifier(uniqueIdentifier);
        body.setVerificationCode(verificationCode);
        return body;
    }

    // 通常可以直接使用 @Transactional 而不需要显式地添加 @EnableTransactionManagement
    // 因为 SpringBoot 的自动配置功能会为你配置一个合适的事务管理器（基于你的数据源配置），并且默认就开启了声明式事务管理
    // rollbackFor = Exception.class  指定Exception，这样即使系统抛出非RuntimeException，也能让事务正常回滚（可用IOException测试）
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void register(SystemLoginReq systemLoginReq) {
        verifyVerificationCode(systemLoginReq);

        SystemUser systemUser = systemUserLogic.getActiveUserByUserName(systemLoginReq.getUserName());
        if (systemUser != null) {
            throw new FreeRuntimeException(-1, "当前用户已存在");
        }

        int uid = systemUserLogic.addUser(systemLoginReq.getUserName());
        SystemUserAuth systemUserAuth = new SystemUserAuth();
        systemUserAuth.setUid(uid);
        systemUserAuth.setPassword(Encipher.encryptWithHmacSHA256(systemLoginReq.getPassword()));
        systemUserAuthLogic.addUserAuth(systemUserAuth);

        redisService.delete(RedisKeyPrefixEnum.getVerifyCode(systemLoginReq.getUniqueIdentifier()));
    }

    @Override
    public SystemLoginResBody login(SystemLoginReq systemLoginReq) {
        verifyVerificationCode(systemLoginReq);

        SystemUser systemUser = systemUserLogic.getActiveUserByUserName(systemLoginReq.getUserName());
        if (systemUser == null) {
            throw new FreeRuntimeException(-1, "当前用户不存在");
        }

        SystemUserAuth systemUserAuth = systemUserAuthLogic.getUserAuthByUserId(systemUser.getId());
        if (systemUserAuth == null) {
            throw new FreeRuntimeException(-1, "当前用户认证信息不存在");
        }

        verifyPassword(systemLoginReq.getPassword(), systemUserAuth.getPassword());
        redisService.delete(RedisKeyPrefixEnum.getVerifyCode(systemLoginReq.getUniqueIdentifier()));

        SystemLoginResBody systemLoginResBody = SystemUserMapstructMapper.INSTANCE.toSystemLoginResBody(systemUser);
        String token = Encipher.encryptWithHmacSHA256(systemUser.getUserId() + System.currentTimeMillis());
        systemLoginResBody.setToken(token);

        // TODO token存储到redis中，用户信息
        // TODO 登录失败达到次数可以锁住用户
        // TODO 可以考虑做个单点登录，redis存储 用户名 -> 当前token，每次登录更新token，旧token就失效，访问就跳转到登录页面

        return systemLoginResBody;
    }

    /**
     * 校验验证码
     *
     * @param systemLoginReq 验证信息
     */
    private void verifyVerificationCode(SystemLoginReq systemLoginReq) {
        String verificationCodeFromRedis = redisService.getValue(RedisKeyPrefixEnum.getVerifyCode(systemLoginReq.getUniqueIdentifier()));
        boolean pass = StringUtils.equals(systemLoginReq.getVerificationCode(), verificationCodeFromRedis);
        if (!pass) {
            log.error("输入的验证码: [{}], 生成的验证码: [{}]", systemLoginReq.getVerificationCode(), verificationCodeFromRedis);
            throw new FreeRuntimeException(-1, "验证码错误或已失效");
        }
    }

    /**
     * 校验密码
     *
     * @param inputPassword        用户输入的密码
     * @param passwordFromDatabase 数据库中存储的密码
     */
    private void verifyPassword(String inputPassword, String passwordFromDatabase) {
        String encryptedPassword = Encipher.encryptWithHmacSHA256(inputPassword);
        boolean pass = Objects.equals(encryptedPassword, passwordFromDatabase);
        if (!pass) {
            throw new FreeRuntimeException(-1, "密码错误");
        }
    }

}
